package com.patsnap.data.npd.dw.etl.utils;

import com.alibaba.fastjson.JSONObject;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class HttpUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtil.class);

    /**
     * 连接池同时最大执行数量
     */
    private static final int POOL_MAX = 200;
    /**
     * 连接池每个连接的最大路由次数
     */
    private static final int POOL_MAX_PERROUTE = 100;

    private static CloseableHttpClient httpClient;

    static {
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(10000)
                .setConnectTimeout(10000)
                .setConnectionRequestTimeout(10000)
                .build();// 设置请求和传输超时时间
        // https验证
        SSLContext sslcontext = null;
        try {
            sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        HostnameVerifier hostnameVerifier = SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslsf).build();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

        cm.setMaxTotal(POOL_MAX);
        cm.setDefaultMaxPerRoute(POOL_MAX_PERROUTE);
        httpClient=HttpClients.custom()
                .setConnectionManager(cm)
                .setDefaultRequestConfig(requestConfig)
                .setRetryHandler(new DefaultHttpRequestRetryHandler())
                .build();
    }

    public static String doPost(String url, JSONObject postParam, Map<String,String> headers) throws Exception {
        try{
            OkHttpClient client = new OkHttpClient().newBuilder()
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .build();
            MediaType mediaType = MediaType.parse("application/json");
            RequestBody body = RequestBody.create(mediaType, postParam.toJSONString());
            Request.Builder builder = new Request.Builder()
                    .url(url)
                    .method("POST", body);
            headers.forEach(builder::addHeader);
            Request request = builder.build();
            long startTime = System.currentTimeMillis();
            try(Response response = client.newCall(request).execute()){
                LOGGER.info("request use time: " + (System.currentTimeMillis() - startTime));
                if(response.isSuccessful()){
                    return response.body().string();
                }else{
                    throw new Exception("do post request api failed,url: " + url + " | param: " + postParam.toJSONString() + " | status: " + response.code());
                }
            }catch (Exception e){
                throw new Exception("do post request cause error, url: " + url + " | param: " + postParam.toJSONString() + " | err: " + e.getMessage());
            }
        }catch (Exception e){
            throw new Exception("do post request cause error, url: " + url + " | param: " + postParam.toJSONString() + " | err: " + e.getMessage());
        }
    }

    public static String doPost(Map<String, String> map, String url, JSONObject jsonObject, List<Header> headers){
        HttpPost httpPost = new HttpPost(url);
        for (Header header : headers) {
            httpPost.addHeader(header);
        }
        return doPost(map, httpPost, jsonObject,url);
    }

    public static String doPost(Map<String,String> map, HttpPost httpPost, JSONObject jsonObject,String url){
        String result = null;
        CloseableHttpResponse response=null;
        InputStream instream=null;
        ByteArrayOutputStream outStream=null;
        AtomicInteger atomicInteger=new AtomicInteger(0);
        AtomicBoolean atomicBoolean=new AtomicBoolean(false);
        do{
            try{
                List<NameValuePair> list = new ArrayList<NameValuePair>();
                Iterator iterator = map.entrySet().iterator();
                while(iterator.hasNext()){
                    Map.Entry<String,String> elem = (Map.Entry<String, String>) iterator.next();
                    list.add(new BasicNameValuePair(elem.getKey(),elem.getValue()));
                }
                if(list.size() > 0){
                    UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list,"utf-8");
                    httpPost.setEntity(entity);
                }
                if(jsonObject != null){
                    StringEntity entity = new StringEntity(jsonObject.toString(),"utf-8");//解决中文乱码问题
                    entity.setContentEncoding("UTF-8");
                    entity.setContentType("application/json");
                    httpPost.setEntity(entity);
                }
                response = httpClient.execute(httpPost);
                if(response != null){
                    HttpEntity resEntity = response.getEntity();
                    if(resEntity != null){
                        instream= resEntity.getContent();
                        outStream = new ByteArrayOutputStream();
                        result= inputStreamTOString(instream,outStream);
                    }
                }
            }catch (NoHttpResponseException e){
                LOGGER.error("超时重试次数:"+atomicInteger.get(),e);
                atomicBoolean.set(true);
                atomicInteger.incrementAndGet();
            }catch(Exception ex){
                LOGGER.error("post error url:"+url,ex);
            }finally {
                try {
                    if(instream!=null){
                        instream.close();
                    }
                    if(outStream!=null){
                        outStream.close();
                    }
                } catch (IOException e) {
                    LOGGER.error("流关闭失败 url"+url,e);
                }
                if(response!=null){
                    try {
                        response.close();
                    } catch (IOException e) {
                        LOGGER.error("dopost CloseableHttpResponse close error url"+url,e);
                    }
                }
                httpPost.releaseConnection();
            }
        }while (atomicInteger.get()<3 && atomicBoolean.get());
        if(atomicInteger.get()>=3){
            LOGGER.error("超时异常");
        }
        return result;
    }

    public static String doPost(Map<String,String> map, String url, String bodyString){
        HttpPost httpPost=new HttpPost(url);
        String result = null;
        CloseableHttpResponse response=null;
        InputStream instream=null;
        ByteArrayOutputStream outStream=null;
        AtomicInteger atomicInteger=new AtomicInteger(0);
        AtomicBoolean atomicBoolean=new AtomicBoolean(false);
        do{
            try{
                List<NameValuePair> list = new ArrayList<NameValuePair>();
                Iterator iterator = map.entrySet().iterator();
                while(iterator.hasNext()){
                    Map.Entry<String,String> elem = (Map.Entry<String, String>) iterator.next();
                    list.add(new BasicNameValuePair(elem.getKey(),elem.getValue()));
                }
                if(list.size() > 0){
                    UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list,"utf-8");
                    httpPost.setEntity(entity);
                }
                if(StringUtils.isNotBlank(bodyString)){
                    StringEntity entity = new StringEntity(bodyString,"utf-8");//解决中文乱码问题
                    entity.setContentEncoding("UTF-8");
                    entity.setContentType("application/json");
                    httpPost.setEntity(entity);
                }
                response = httpClient.execute(httpPost);
                if(response != null){
                    HttpEntity resEntity = response.getEntity();
                    if(resEntity != null){
                        instream= resEntity.getContent();
                        outStream = new ByteArrayOutputStream();
                        result= inputStreamTOString(instream,outStream);
                    }
                }
            }catch (NoHttpResponseException e){
                LOGGER.error("超时重试次数:"+atomicInteger.get(),e);
                atomicBoolean.set(true);
                atomicInteger.incrementAndGet();
            }catch(Exception ex){
                LOGGER.error("post error url:"+url,ex);
            }finally {
                try {
                    if(instream!=null){
                        instream.close();
                    }
                    if(outStream!=null){
                        outStream.close();
                    }
                } catch (IOException e) {
                    LOGGER.error("流关闭失败 url"+url,e);
                }
                if(response!=null){
                    try {
                        response.close();
                    } catch (IOException e) {
                        LOGGER.error("dopost CloseableHttpResponse close error url"+url,e);
                    }
                }
                httpPost.releaseConnection();
            }
        }while (atomicInteger.get()<3 && atomicBoolean.get());
        if(atomicInteger.get()>=3){
            LOGGER.error("超时异常");
        }
        return result;
    }

    public static String doGet(String  url,Map<String,String> params,List<Header> headers){
        String result = "";
        HttpGet httpGet=new HttpGet(url);
        for (Header header : headers) {
            httpGet.addHeader(header);
        }
        CloseableHttpResponse response=null;
        InputStream instream=null;
        ByteArrayOutputStream outStream=null;
        AtomicInteger atomicInteger=new AtomicInteger(0);
        AtomicBoolean atomicBoolean=new AtomicBoolean(false);
        do{
            try{
                if (params != null && !params.isEmpty()) {
                    List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());
                    for (String key : params.keySet()) {
                        pairs.add(new BasicNameValuePair(key, params.get(key)));
                    }
                    if(url.contains("?")){
                        url += "&" +EntityUtils.toString(new UrlEncodedFormEntity(pairs, "utf-8"));
                    }else{
                        url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, "utf-8"));
                    }
                    httpGet.setURI(URI.create(url));
                }
                response = httpClient.execute(httpGet);
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                    if(response != null){
                        HttpEntity resEntity = response.getEntity();
                        if(resEntity != null){
                            instream= resEntity.getContent();
                            outStream = new ByteArrayOutputStream();
                            result= inputStreamTOString(instream,outStream);
                        }
                    }
                }
            }catch (NoHttpResponseException e){
                LOGGER.error("超时NoHttpResponseException:" + url, e);
                atomicBoolean.set(true);
                atomicInteger.incrementAndGet();
            } catch (UnsupportedEncodingException e) {
                LOGGER.error("", e);
            } catch (ClientProtocolException e) {
                LOGGER.error("", e);
            } catch (IOException e) {
                LOGGER.error("", e);
            } catch (Exception e) {
                LOGGER.error("", e);
            } finally {
                if(instream!=null){
                    try {
                        instream.close();
                    } catch (IOException e) {
                    }
                }
                if(outStream!=null){
                    try {
                        outStream.close();
                    } catch (IOException e) {
                    }
                }
                if(response!=null){
                    try {
                        response.close();
                    } catch (IOException e) {
                    }
                }
                httpGet.releaseConnection();
            }
        }while (atomicInteger.get()<3 && atomicBoolean.get());
        if(atomicInteger.get()>=3){
            LOGGER.error("超时异常");
        }
        return result;
    }

    public static String doDelete(String url) {
        HttpDelete httpDelete = new HttpDelete(url);
        String result = null;
        CloseableHttpResponse response = null;
        InputStream instream = null;
        ByteArrayOutputStream outStream = null;
        AtomicInteger atomicInteger = new AtomicInteger(0);
        AtomicBoolean atomicBoolean = new AtomicBoolean(false);
        do {
            try {
                response = httpClient.execute(httpDelete);
                if (response != null) {
                    HttpEntity resEntity = response.getEntity();
                    if (resEntity != null) {
                        instream = resEntity.getContent();
                        outStream = new ByteArrayOutputStream();
                        result = inputStreamTOString(instream, outStream);
                    }
                }
            } catch (NoHttpResponseException e) {
                LOGGER.error("超时重试次数:" + atomicInteger.get(), e);
                atomicBoolean.set(true);
                atomicInteger.incrementAndGet();
            } catch (Exception ex) {
                LOGGER.error("put error url:" + url, ex);
            } finally {
                try {
                    if (instream != null) {
                        instream.close();
                    }
                    if (outStream != null) {
                        outStream.close();
                    }
                } catch (IOException e) {
                    LOGGER.error("流关闭失败 url" + url, e);
                }
                if (response != null) {
                    try {
                        response.close();
                    } catch (IOException e) {
                        LOGGER.error("dopost CloseableHttpResponse close error url" + url, e);
                    }
                }
                httpDelete.releaseConnection();
            }
        } while (atomicInteger.get() < 3 && atomicBoolean.get());
        if (atomicInteger.get() >= 3) {
            LOGGER.error("超时异常");
        }
        return result;
    }


    private static String inputStreamTOString(InputStream in,ByteArrayOutputStream outputStream) throws Exception{
        byte[] data = new byte[1024];
        int count = -1;
        while((count = in.read(data,0,1024)) != -1)
            outputStream.write(data, 0, count);
        data = null;
        return new String(outputStream.toByteArray(),"utf-8");
    }

}
